import 'package:logger/logger.dart';

import '../dao/ImModuleDao.dart';
import '../dao/ImModuleItemDao.dart';
import '../pojo/ImPojo.dart';
import '../socket/dto/business/PullData2.dart';
import 'BaseService.dart';

class ImModuleService extends BaseService {
  final _logger = Logger(printer: PrettyPrinter(),);

  // final _moduleDao = ImModuleDao();
  // final _moduleItemDao = ImModuleItemDao();

  Future<List<ImModule>> saveOrUpdates(String userId, List<ModuleItem> moduleItemList) async {
    if (moduleItemList.isEmpty) return Future.value([]);
    var imModules = moduleItemList.map((moduleItem)=>_toModule(userId, moduleItem)).toList();

    final moduleDao = ImModuleDao();
    final moduleItemDao = ImModuleItemDao();

    List<String> codeList = moduleItemList.map((e) => e.name).toList();
    List<ImModule> dbModules = await moduleDao.getByNames(codeList);

    Map<String, ImModule> dbNameMap = Map.fromIterable(dbModules, key: (module)=>module.name, value: (module)=>module);
    List<ImModule> insertModules = imModules.where((g) => dbNameMap[g.name] == null).toList();
    List<ImModule> updateModules = imModules.where((g) => dbNameMap[g.name] != null).toList();

    updateModules.forEach((module){
      ImModule? dbModule = dbNameMap[module.name];
      module.id = dbModule!.id;
      module.moduleItemList!.forEach((mi)=>mi.moduleId = dbModule!.id);
    });

    List<ImModuleItem> insertModuleItems = <ImModuleItem>[];
    List<ImModuleItem> updateModuleItems = <ImModuleItem>[];

    await moduleDao.batchInserts(insertModules);
    await moduleDao.batchUpdates(updateModules);

    insertModules.forEach((module){
      if (module.moduleItemList != null) {
        module.moduleItemList!.forEach((mi)=>mi.moduleId = module.id);
        insertModuleItems.addAll(module.moduleItemList!);
      }
    });

    for(var module in updateModules) {
      if (module.moduleItemList == null) continue;
      List<String> names = module.moduleItemList!.map((mi)=>mi.name).toList();
      List<ImModuleItem> moduleItemList = await moduleItemDao.getByNames(names);
      Set<String> nameSet = moduleItemList.map((mi)=>mi.name).toSet();
      List<ImModuleItem> insets = moduleItemList.where((g)=>!nameSet.contains(g.name)).toList();
      List<ImModuleItem> updates = moduleItemList.where((g)=>nameSet.contains(g.name)).toList();

      insertModuleItems.addAll(insets);
      updateModuleItems.addAll(updates);
    }

    await moduleItemDao.batchInserts(insertModuleItems);
    await moduleItemDao.batchUpdates(updateModuleItems);

    return imModules;
  }

  Future<void> saveOrUpdate(ImModule module) async {
    final moduleDao = ImModuleDao();
    final moduleItemDao = ImModuleItemDao();

    ImModule? dbModule = await moduleDao.getByName(module.name);
    if (dbModule == null) {
      moduleDao.batchInserts([module]);
      module.moduleItemList!.forEach((moduleItem)=>moduleItem.moduleId = module.id);
      moduleItemDao.batchInserts(module.moduleItemList!);
    } else {
      for (var moduleItem in module.moduleItemList!) {
        moduleItem.moduleId = dbModule.id!;
        ImModuleItem? dbModuleItem = await moduleItemDao.getByModuleIdAndName(dbModule.id!, moduleItem.name);
        if (dbModuleItem != null) {
          await moduleItemDao.batchUpdates([moduleItem]);
        } else {
          await moduleItemDao.batchInserts([moduleItem]);
        }
      }
    }
  }

  Future<void> update(ImModule imModule) async {
    final moduleDao = ImModuleDao();
    await moduleDao.batchUpdates([imModule]);
  }

  Future<List<ImModule>> getModules() async {
    final moduleDao = ImModuleDao();

    List<ImModule>? dbModules = await moduleDao.getAll();
    dbModules = dbModules.where((module)=>!module.name.endsWith('_history')).toList();
    return dbModules;
  }

  Future<ImModule?> getHistoryModule(String userId) async {
    final moduleDao = ImModuleDao();
    final moduleItemDao = ImModuleItemDao();

    ImModule? module = await moduleDao.getByName(userId + '_history');
    if (module != null) {
      List<ImModuleItem> moduleItems = await moduleItemDao.getByModuleId(module.id!);
      if (moduleItems.length > 4) {
        moduleItems = moduleItems.sublist(0, 4);
      }
      List<ImModuleItem> newImModuleItems = <ImModuleItem>[];
      for (var moduleItem in moduleItems) {
        int firstIndex = moduleItem.name.indexOf("_");
        String moduleIdStr = moduleItem.name.substring(0, firstIndex);
        String moduleItemName = moduleItem.name.substring(firstIndex+1);
        ImModuleItem? dbModuleItem = await moduleItemDao.getByModuleIdAndName(int.parse(moduleIdStr), moduleItemName);
        newImModuleItems.add(dbModuleItem!);

      }
      module.moduleItemList = newImModuleItems;
    }
    return module;
  }

  ImModule _toModule(String userId, ModuleItem moduleItem) {
    ImModule module = new ImModule(name: moduleItem.name,
      no: moduleItem.no,
      description: moduleItem.description,
      status: ImModuleStatus.ofValue(moduleItem.status.value),
    );
    if (moduleItem.moduleItemInfoList != null) {
      List<ImModuleItem> moduleItems = moduleItem.moduleItemInfoList!.map((mi)=>_toModuleItem(mi)).toList();
      module.moduleItemList = moduleItems;
    }
    return module;
  }

  ImModuleItem _toModuleItem(ModuleItemInfo moduleItemInfo) {
    ImModuleItem moduleItem = new ImModuleItem(name: moduleItemInfo.name,
      icon: moduleItemInfo.icon,
      no: moduleItemInfo.no,
      type: ImModuleItemType.ofValue(moduleItemInfo.type.value),
      status: ImModuleItemStatus.ofValue(moduleItemInfo.status.value)
    );
    moduleItem.description = moduleItemInfo.description;
    moduleItem.url = moduleItemInfo.url;
    moduleItem.params = moduleItemInfo.params;
    return moduleItem;
  }
}